/**
 * jquery.Jcrop.js v0.9.10
 * jQuery Image Cropping Plugin - released under MIT License 
 * Author: Kelly Hallman <khallman@gmail.com>
 * http://github.com/tapmodo/Jcrop
 * Copyright (c) 2008-2012 Tapmodo Interactive LLC {{{
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * }}}
 */

(function ($) {

  $.Jcrop = function (obj, opt) {
	var options = $.extend({}, $.Jcrop.defaults),
		docOffset, lastcurs, ie6mode = false;

	// Internal Methods {{{
	function px(n) {
	  return n + 'px';
	}
	function cssClass(cl) {
	  return options.baseClass + '-' + cl;
	}
	function supportsColorFade() {
	  return $.fx.step.hasOwnProperty('backgroundColor');
	}
	function getPos(obj) //{{{
	{
	  var pos = $(obj).offset();
	  return [pos.left, pos.top];
	}
	//}}}
	function mouseAbs(e) //{{{
	{
	  return [(e.pageX - docOffset[0]), (e.pageY - docOffset[1])];
	}
	//}}}
	function setOptions(opt) //{{{
	{
	  if (typeof(opt) !== 'object') opt = {};
	  options = $.extend(options, opt);

	  $.each(['onChange','onSelect','onRelease','onDblClick'],function(i,e) {
		if (typeof(options[e]) !== 'function') options[e] = function () {};
	  });
	}
	//}}}
	function startDragMode(mode, pos) //{{{
	{
	  docOffset = getPos($img);
	  Tracker.setCursor(mode === 'move' ? mode : mode + '-resize');

	  if (mode === 'move') {
		return Tracker.activateHandlers(createMover(pos), doneSelect);
	  }

	  var fc = Coords.getFixed();
	  var opp = oppLockCorner(mode);
	  var opc = Coords.getCorner(oppLockCorner(opp));

	  Coords.setPressed(Coords.getCorner(opp));
	  Coords.setCurrent(opc);

	  Tracker.activateHandlers(dragmodeHandler(mode, fc), doneSelect);
	}
	//}}}
	function dragmodeHandler(mode, f) //{{{
	{
	  return function (pos) {
		if (!options.aspectRatio) {
		  switch (mode) {
		  case 'e':
			pos[1] = f.y2;
			break;
		  case 'w':
			pos[1] = f.y2;
			break;
		  case 'n':
			pos[0] = f.x2;
			break;
		  case 's':
			pos[0] = f.x2;
			break;
		  }
		} else {
		  switch (mode) {
		  case 'e':
			pos[1] = f.y + 1;
			break;
		  case 'w':
			pos[1] = f.y + 1;
			break;
		  case 'n':
			pos[0] = f.x + 1;
			break;
		  case 's':
			pos[0] = f.x + 1;
			break;
		  }
		}
		Coords.setCurrent(pos);
		Selection.update();
	  };
	}
	//}}}
	function createMover(pos) //{{{
	{
	  var lloc = pos;
	  KeyManager.watchKeys();

	  return function (pos) {
		Coords.moveOffset([pos[0] - lloc[0], pos[1] - lloc[1]]);
		lloc = pos;

		Selection.update();
	  };
	}
	//}}}
	function oppLockCorner(ord) //{{{
	{
	  switch (ord) {
	  case 'n':
		return 'sw';
	  case 's':
		return 'nw';
	  case 'e':
		return 'nw';
	  case 'w':
		return 'ne';
	  case 'ne':
		return 'sw';
	  case 'nw':
		return 'se';
	  case 'se':
		return 'nw';
	  case 'sw':
		return 'ne';
	  }
	}
	//}}}
	function createDragger(ord) //{{{
	{
	  return function (e) {
		if (options.disabled) {
		  return false;
		}
		if ((ord === 'move') && !options.allowMove) {
		  return false;
		}
		
		// Fix position of crop area when dragged the very first time.
		// Necessary when crop image is in a hidden element when page is loaded.
		docOffset = getPos($img);

		btndown = true;
		startDragMode(ord, mouseAbs(e));
		e.stopPropagation();
		e.preventDefault();
		return false;
	  };
	}
	//}}}
	function presize($obj, w, h) //{{{
	{
	  var nw = $obj.width(),
		  nh = $obj.height();
	  if ((nw > w) && w > 0) {
		nw = w;
		nh = (w / $obj.width()) * $obj.height();
	  }
	  if ((nh > h) && h > 0) {
		nh = h;
		nw = (h / $obj.height()) * $obj.width();
	  }
	  xscale = $obj.width() / nw;
	  yscale = $obj.height() / nh;
	  $obj.width(nw).height(nh);
	}
	//}}}
	function unscale(c) //{{{
	{
	  return {
		x: c.x * xscale,
		y: c.y * yscale,
		x2: c.x2 * xscale,
		y2: c.y2 * yscale,
		w: c.w * xscale,
		h: c.h * yscale
	  };
	}
	//}}}
	function doneSelect(pos) //{{{
	{
	  var c = Coords.getFixed();
	  if ((c.w > options.minSelect[0]) && (c.h > options.minSelect[1])) {
		Selection.enableHandles();
		Selection.done();
	  } else {
		Selection.release();
	  }
	  Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
	}
	//}}}
	function newSelection(e) //{{{
	{
	  if (options.disabled) {
		return false;
	  }
	  if (!options.allowSelect) {
		return false;
	  }
	  btndown = true;
	  docOffset = getPos($img);
	  Selection.disableHandles();
	  Tracker.setCursor('crosshair');
	  var pos = mouseAbs(e);
	  Coords.setPressed(pos);
	  Selection.update();
	  Tracker.activateHandlers(selectDrag, doneSelect);
	  KeyManager.watchKeys();

	  e.stopPropagation();
	  e.preventDefault();
	  return false;
	}
	//}}}
	function selectDrag(pos) //{{{
	{
	  Coords.setCurrent(pos);
	  Selection.update();
	}
	//}}}
	function newTracker() //{{{
	{
	  var trk = $('<div></div>').addClass(cssClass('tracker'));
	  if ($.browser.msie) {
		trk.css({
		  opacity: 0,
		  backgroundColor: 'white'
		});
	  }
	  return trk;
	}
	//}}}

	// }}}
	// Initialization {{{
	// Sanitize some options {{{
	if ($.browser.msie && ($.browser.version.split('.')[0] === '6')) {
	  ie6mode = true;
	}
	if (typeof(obj) !== 'object') {
	  obj = $(obj)[0];
	}
	if (typeof(opt) !== 'object') {
	  opt = {};
	}
	// }}}
	setOptions(opt);
	// Initialize some jQuery objects {{{
	// The values are SET on the image(s) for the interface
	// If the original image has any of these set, they will be reset
	// However, if you destroy() the Jcrop instance the original image's
	// character in the DOM will be as you left it.
	var img_css = {
	  border: 'none',
	  visibility: 'visible',
	  margin: 0,
	  padding: 0,
	  position: 'absolute',
	  top: 0,
	  left: 0
	};

	var $origimg = $(obj),
	  img_mode = true;

	if (obj.tagName == 'IMG') {
	  // Fix size of crop image.
	  // Necessary when crop image is within a hidden element when page is loaded.
	  if ($origimg[0].width != 0 && $origimg[0].height != 0) {
		// Obtain dimensions from contained img element.
		$origimg.width($origimg[0].width);
		$origimg.height($origimg[0].height);
	  } else {
		// Obtain dimensions from temporary image in case the original is not loaded yet (e.g. IE 7.0). 
		var tempImage = new Image();
		tempImage.src = $origimg[0].src;
		$origimg.width(tempImage.width);
		$origimg.height(tempImage.height);
	  } 

	  var $img = $origimg.clone().removeAttr('id').css(img_css).show();

	  $img.width($origimg.width());
	  $img.height($origimg.height());
	  $origimg.after($img).hide();

	} else {
	  $img = $origimg.css(img_css).show();
	  img_mode = false;
	  if (options.shade === null) { options.shade = true; }
	}

	presize($img, options.boxWidth, options.boxHeight);

	var boundx = $img.width(),
		boundy = $img.height(),
		
		
		$div = $('<div />').width(boundx).height(boundy).addClass(cssClass('holder')).css({
		position: 'relative',
		backgroundColor: options.bgColor
	  }).insertAfter($origimg).append($img);

	if (options.addClass) {
	  $div.addClass(options.addClass);
	}

	var $img2 = $('<div />'),

		$img_holder = $('<div />') 
		.width('100%').height('100%').css({
		  zIndex: 310,
		  position: 'absolute',
		  overflow: 'hidden'
		}),

		$hdl_holder = $('<div />') 
		.width('100%').height('100%').css('zIndex', 320), 

		$sel = $('<div />') 
		.css({
		  position: 'absolute',
		  zIndex: 600
		}).dblclick(function(){
		  var c = Coords.getFixed();
		  options.onDblClick.call(api,c);
		}).insertBefore($img).append($img_holder, $hdl_holder); 

	if (img_mode) {

	  $img2 = $('<img />')
		  .attr('src', $img.attr('src')).css(img_css).width(boundx).height(boundy),

	  $img_holder.append($img2);

	}

	if (ie6mode) {
	  $sel.css({
		overflowY: 'hidden'
	  });
	}

	var bound = options.boundary;
	var $trk = newTracker().width(boundx + (bound * 2)).height(boundy + (bound * 2)).css({
	  position: 'absolute',
	  top: px(-bound),
	  left: px(-bound),
	  zIndex: 290
	}).mousedown(newSelection);

	/* }}} */
	// Set more variables {{{
	var bgcolor = options.bgColor,
		bgopacity = options.bgOpacity,
		xlimit, ylimit, xmin, ymin, xscale, yscale, enabled = true,
		btndown, animating, shift_down;

	docOffset = getPos($img);
	// }}}
	// }}}
	// Internal Modules {{{
	// Touch Module {{{ 
	var Touch = (function () {
	  // Touch support detection function adapted (under MIT License)
	  // from code by Jeffrey Sambells - http://github.com/iamamused/
	  function hasTouchSupport() {
		var support = {},
			events = ['touchstart', 'touchmove', 'touchend'],
			el = document.createElement('div'), i;

		try {
		  for(i=0; i<events.length; i++) {
			var eventName = events[i];
			eventName = 'on' + eventName;
			var isSupported = (eventName in el);
			if (!isSupported) {
			  el.setAttribute(eventName, 'return;');
			  isSupported = typeof el[eventName] == 'function';
			}
			support[events[i]] = isSupported;
		  }
		  return support.touchstart && support.touchend && support.touchmove;
		}
		catch(err) {
		  return false;
		}
	  }

	  function detectSupport() {
		if ((options.touchSupport === true) || (options.touchSupport === false)) return options.touchSupport;
		  else return hasTouchSupport();
	  }
	  return {
		createDragger: function (ord) {
		  return function (e) {
			e.pageX = e.originalEvent.changedTouches[0].pageX;
			e.pageY = e.originalEvent.changedTouches[0].pageY;
			if (options.disabled) {
			  return false;
			}
			if ((ord === 'move') && !options.allowMove) {
			  return false;
			}
			btndown = true;
			startDragMode(ord, mouseAbs(e));
			e.stopPropagation();
			e.preventDefault();
			return false;
		  };
		},
		newSelection: function (e) {
		  e.pageX = e.originalEvent.changedTouches[0].pageX;
		  e.pageY = e.originalEvent.changedTouches[0].pageY;
		  return newSelection(e);
		},
		isSupported: hasTouchSupport,
		support: detectSupport()
	  };
	}());
	// }}}
	// Coords Module {{{
	var Coords = (function () {
	  var x1 = 0,
		  y1 = 0,
		  x2 = 0,
		  y2 = 0,
		  ox, oy;

	  function setPressed(pos) //{{{
	  {
		pos = rebound(pos);
		x2 = x1 = pos[0];
		y2 = y1 = pos[1];
	  }
	  //}}}
	  function setCurrent(pos) //{{{
	  {
		pos = rebound(pos);
		ox = pos[0] - x2;
		oy = pos[1] - y2;
		x2 = pos[0];
		y2 = pos[1];
	  }
	  //}}}
	  function getOffset() //{{{
	  {
		return [ox, oy];
	  }
	  //}}}
	  function moveOffset(offset) //{{{
	  {
		var ox = offset[0],
			oy = offset[1];

		if (0 > x1 + ox) {
		  ox -= ox + x1;
		}
		if (0 > y1 + oy) {
		  oy -= oy + y1;
		}

		if (boundy < y2 + oy) {
		  oy += boundy - (y2 + oy);
		}
		if (boundx < x2 + ox) {
		  ox += boundx - (x2 + ox);
		}

		x1 += ox;
		x2 += ox;
		y1 += oy;
		y2 += oy;
	  }
	  //}}}
	  function getCorner(ord) //{{{
	  {
		var c = getFixed();
		switch (ord) {
		case 'ne':
		  return [c.x2, c.y];
		case 'nw':
		  return [c.x, c.y];
		case 'se':
		  return [c.x2, c.y2];
		case 'sw':
		  return [c.x, c.y2];
		}
	  }
	  //}}}
	  function getFixed() //{{{
	  {
		if (!options.aspectRatio) {
		  return getRect();
		}
		// This function could use some optimization I think...
		var aspect = options.aspectRatio,
			min_x = options.minSize[0] / xscale,
			
			
			//min_y = options.minSize[1]/yscale,
			max_x = options.maxSize[0] / xscale,
			max_y = options.maxSize[1] / yscale,
			rw = x2 - x1,
			rh = y2 - y1,
			rwa = Math.abs(rw),
			rha = Math.abs(rh),
			real_ratio = rwa / rha,
			xx, yy, w, h;

		if (max_x === 0) {
		  max_x = boundx * 10;
		}
		if (max_y === 0) {
		  max_y = boundy * 10;
		}
		if (real_ratio < aspect) {
		  yy = y2;
		  w = rha * aspect;
		  xx = rw < 0 ? x1 - w : w + x1;

		  if (xx < 0) {
			xx = 0;
			h = Math.abs((xx - x1) / aspect);
			yy = rh < 0 ? y1 - h : h + y1;
		  } else if (xx > boundx) {
			xx = boundx;
			h = Math.abs((xx - x1) / aspect);
			yy = rh < 0 ? y1 - h : h + y1;
		  }
		} else {
		  xx = x2;
		  h = rwa / aspect;
		  yy = rh < 0 ? y1 - h : y1 + h;
		  if (yy < 0) {
			yy = 0;
			w = Math.abs((yy - y1) * aspect);
			xx = rw < 0 ? x1 - w : w + x1;
		  } else if (yy > boundy) {
			yy = boundy;
			w = Math.abs(yy - y1) * aspect;
			xx = rw < 0 ? x1 - w : w + x1;
		  }
		}

		// Magic %-)
		if (xx > x1) { // right side
		  if (xx - x1 < min_x) {
			xx = x1 + min_x;
		  } else if (xx - x1 > max_x) {
			xx = x1 + max_x;
		  }
		  if (yy > y1) {
			yy = y1 + (xx - x1) / aspect;
		  } else {
			yy = y1 - (xx - x1) / aspect;
		  }
		} else if (xx < x1) { // left side
		  if (x1 - xx < min_x) {
			xx = x1 - min_x;
		  } else if (x1 - xx > max_x) {
			xx = x1 - max_x;
		  }
		  if (yy > y1) {
			yy = y1 + (x1 - xx) / aspect;
		  } else {
			yy = y1 - (x1 - xx) / aspect;
		  }
		}

		if (xx < 0) {
		  x1 -= xx;
		  xx = 0;
		} else if (xx > boundx) {
		  x1 -= xx - boundx;
		  xx = boundx;
		}

		if (yy < 0) {
		  y1 -= yy;
		  yy = 0;
		} else if (yy > boundy) {
		  y1 -= yy - boundy;
		  yy = boundy;
		}

		return makeObj(flipCoords(x1, y1, xx, yy));
	  }
	  //}}}
	  function rebound(p) //{{{
	  {
		if (p[0] < 0) {
		  p[0] = 0;
		}
		if (p[1] < 0) {
		  p[1] = 0;
		}

		if (p[0] > boundx) {
		  p[0] = boundx;
		}
		if (p[1] > boundy) {
		  p[1] = boundy;
		}

		return [p[0], p[1]];
	  }
	  //}}}
	  function flipCoords(x1, y1, x2, y2) //{{{
	  {
		var xa = x1,
			xb = x2,
			ya = y1,
			yb = y2;
		if (x2 < x1) {
		  xa = x2;
		  xb = x1;
		}
		if (y2 < y1) {
		  ya = y2;
		  yb = y1;
		}
		return [xa, ya, xb, yb];
	  }
	  //}}}
	  function getRect() //{{{
	  {
		var xsize = x2 - x1,
			ysize = y2 - y1,
			delta;

		if (xlimit && (Math.abs(xsize) > xlimit)) {
		  x2 = (xsize > 0) ? (x1 + xlimit) : (x1 - xlimit);
		}
		if (ylimit && (Math.abs(ysize) > ylimit)) {
		  y2 = (ysize > 0) ? (y1 + ylimit) : (y1 - ylimit);
		}

		if (ymin / yscale && (Math.abs(ysize) < ymin / yscale)) {
		  y2 = (ysize > 0) ? (y1 + ymin / yscale) : (y1 - ymin / yscale);
		}
		if (xmin / xscale && (Math.abs(xsize) < xmin / xscale)) {
		  x2 = (xsize > 0) ? (x1 + xmin / xscale) : (x1 - xmin / xscale);
		}

		if (x1 < 0) {
		  x2 -= x1;
		  x1 -= x1;
		}
		if (y1 < 0) {
		  y2 -= y1;
		  y1 -= y1;
		}
		if (x2 < 0) {
		  x1 -= x2;
		  x2 -= x2;
		}
		if (y2 < 0) {
		  y1 -= y2;
		  y2 -= y2;
		}
		if (x2 > boundx) {
		  delta = x2 - boundx;
		  x1 -= delta;
		  x2 -= delta;
		}
		if (y2 > boundy) {
		  delta = y2 - boundy;
		  y1 -= delta;
		  y2 -= delta;
		}
		if (x1 > boundx) {
		  delta = x1 - boundy;
		  y2 -= delta;
		  y1 -= delta;
		}
		if (y1 > boundy) {
		  delta = y1 - boundy;
		  y2 -= delta;
		  y1 -= delta;
		}

		return makeObj(flipCoords(x1, y1, x2, y2));
	  }
	  //}}}
	  function makeObj(a) //{{{
	  {
		return {
		  x: a[0],
		  y: a[1],
		  x2: a[2],
		  y2: a[3],
		  w: a[2] - a[0],
		  h: a[3] - a[1]
		};
	  }
	  //}}}

	  return {
		flipCoords: flipCoords,
		setPressed: setPressed,
		setCurrent: setCurrent,
		getOffset: getOffset,
		moveOffset: moveOffset,
		getCorner: getCorner,
		getFixed: getFixed
	  };
	}());

	//}}}
	// Shade Module {{{
	var Shade = (function() {
	  var enabled = false,
		  holder = $('<div />').css({
			position: 'absolute',
			zIndex: 240,
			opacity: 0
		  }),
		  shades = {
			top: createShade(),
			left: createShade().height(boundy),
			right: createShade().height(boundy),
			bottom: createShade()
		  };

	  function resizeShades(w,h) {
		shades.left.css({ height: px(h) });
		shades.right.css({ height: px(h) });
	  }
	  function updateAuto()
	  {
		return updateShade(Coords.getFixed());
	  }
	  function updateShade(c)
	  {
		shades.top.css({
		  left: px(c.x),
		  width: px(c.w),
		  height: px(c.y)
		});
		shades.bottom.css({
		  top: px(c.y2),
		  left: px(c.x),
		  width: px(c.w),
		  height: px(boundy-c.y2)
		});
		shades.right.css({
		  left: px(c.x2),
		  width: px(boundx-c.x2)
		});
		shades.left.css({
		  width: px(c.x)
		});
	  }
	  function createShade() {
		return $('<div />').css({
		  position: 'absolute',
		  backgroundColor: options.shadeColor||options.bgColor
		}).appendTo(holder);
	  }
	  function enableShade() {
		if (!enabled) {
		  enabled = true;
		  holder.insertBefore($img);
		  updateAuto();
		  Selection.setBgOpacity(1,0,1);
		  $img2.hide();

		  setBgColor(options.shadeColor||options.bgColor,1);
		  if (Selection.isAwake())
		  {
			setOpacity(options.bgOpacity,1);
		  }
			else setOpacity(1,1);
		}
	  }
	  function setBgColor(color,now) {
		colorChangeMacro(getShades(),color,now);
	  }
	  function disableShade() {
		if (enabled) {
		  holder.remove();
		  $img2.show();
		  enabled = false;
		  if (Selection.isAwake()) {
			Selection.setBgOpacity(options.bgOpacity,1,1);
		  } else {
			Selection.setBgOpacity(1,1,1);
			Selection.disableHandles();
		  }
		  colorChangeMacro($div,0,1);
		}
	  }
	  function setOpacity(opacity,now) {
		if (enabled) {
		  if (options.bgFade && !now) {
			holder.animate({
			  opacity: 1-opacity
			},{
			  queue: false,
			  duration: options.fadeTime
			});
		  }
		  else holder.css({opacity:1-opacity});
		}
	  }
	  function refreshAll() {
		options.shade ? enableShade() : disableShade();
		if (Selection.isAwake()) setOpacity(options.bgOpacity);
	  }
	  function getShades() {
		return holder.children();
	  }

	  return {
		update: updateAuto,
		updateRaw: updateShade,
		getShades: getShades,
		setBgColor: setBgColor,
		enable: enableShade,
		disable: disableShade,
		resize: resizeShades,
		refresh: refreshAll,
		opacity: setOpacity
	  };
	}());
	// }}}
	// Selection Module {{{
	var Selection = (function () {
	  var awake,
		  hdep = 370,
		  borders = {},
		  handle = {},
		  dragbar = {},
		  seehandles = false;

	  // Private Methods
	  function insertBorder(type) //{{{
	  {
		var jq = $('<div />').css({
		  position: 'absolute',
		  opacity: options.borderOpacity
		}).addClass(cssClass(type));
		$img_holder.append(jq);
		return jq;
	  }
	  //}}}
	  function dragDiv(ord, zi) //{{{
	  {
		var jq = $('<div />').mousedown(createDragger(ord)).css({
		  cursor: ord + '-resize',
		  position: 'absolute',
		  zIndex: zi
		}).addClass('ord-'+ord);

		if (Touch.support) {
		  jq.bind('touchstart.jcrop', Touch.createDragger(ord));
		}

		$hdl_holder.append(jq);
		return jq;
	  }
	  //}}}
	  function insertHandle(ord) //{{{
	  {
		var hs = options.handleSize;
		return dragDiv(ord, hdep++).css({
		  opacity: options.handleOpacity
		}).width(hs).height(hs).addClass(cssClass('handle'));
	  }
	  //}}}
	  function insertDragbar(ord) //{{{
	  {
		return dragDiv(ord, hdep++).addClass('jcrop-dragbar');
	  }
	  //}}}
	  function createDragbars(li) //{{{
	  {
		var i;
		for (i = 0; i < li.length; i++) {
		  dragbar[li[i]] = insertDragbar(li[i]);
		}
	  }
	  //}}}
	  function createBorders(li) //{{{
	  {
		var cl,i;
		for (i = 0; i < li.length; i++) {
		  switch(li[i]){
			case'n': cl='hline'; break;
			case's': cl='hline bottom'; break;
			case'e': cl='vline right'; break;
			case'w': cl='vline'; break;
		  }
		  borders[li[i]] = insertBorder(cl);
		}
	  }
	  //}}}
	  function createHandles(li) //{{{
	  {
		var i;
		for (i = 0; i < li.length; i++) {
		  handle[li[i]] = insertHandle(li[i]);
		}
	  }
	  //}}}
	  function moveto(x, y) //{{{
	  {
		if (!options.shade) {
		  $img2.css({
			top: px(-y),
			left: px(-x)
		  });
		}
		$sel.css({
		  top: px(y),
		  left: px(x)
		});
	  }
	  //}}}
	  function resize(w, h) //{{{
	  {
		$sel.width(w).height(h);
	  }
	  //}}}
	  function refresh() //{{{
	  {
		var c = Coords.getFixed();

		Coords.setPressed([c.x, c.y]);
		Coords.setCurrent([c.x2, c.y2]);

		updateVisible();
	  }
	  //}}}

	  // Internal Methods
	  function updateVisible(select) //{{{
	  {
		if (awake) {
		  return update(select);
		}
	  }
	  //}}}
	  function update(select) //{{{
	  {
		var c = Coords.getFixed();

		resize(c.w, c.h);
		moveto(c.x, c.y);
		if (options.shade) Shade.updateRaw(c);

		awake || show();

		if (select) {
		  options.onSelect.call(api, unscale(c));
		} else {
		  options.onChange.call(api, unscale(c));
		}
	  }
	  //}}}
	  function setBgOpacity(opacity,force,now) //{{{
	  {
		if (!awake && !force) return;
		if (options.bgFade && !now) {
		  $img.animate({
			opacity: opacity
		  },{
			queue: false,
			duration: options.fadeTime
		  });
		} else {
		  $img.css('opacity', opacity);
		}
	  }
	  //}}}
	  function show() //{{{
	  {
		$sel.show();

		if (options.shade) Shade.opacity(bgopacity);
		  else setBgOpacity(bgopacity,true);

		awake = true;
	  }
	  //}}}
	  function release() //{{{
	  {
		disableHandles();
		$sel.hide();

		if (options.shade) Shade.opacity(1);
		  else setBgOpacity(1);

		awake = false;
		options.onRelease.call(api);
	  }
	  //}}}
	  function showHandles() //{{{
	  {
		if (seehandles) {
		  $hdl_holder.show();
		}
	  }
	  //}}}
	  function enableHandles() //{{{
	  {
		seehandles = true;
		if (options.allowResize) {
		  $hdl_holder.show();
		  return true;
		}
	  }
	  //}}}
	  function disableHandles() //{{{
	  {
		seehandles = false;
		$hdl_holder.hide();
	  } 
	  //}}}
	  function animMode(v) //{{{
	  {
		if (animating === v) {
		  disableHandles();
		} else {
		  enableHandles();
		}
	  } 
	  //}}}
	  function done() //{{{
	  {
		animMode(false);
		refresh();
	  } 
	  //}}}
	  // Insert draggable elements {{{
	  // Insert border divs for outline

	  if (options.dragEdges && $.isArray(options.createDragbars))
		createDragbars(options.createDragbars);

	  if ($.isArray(options.createHandles))
		createHandles(options.createHandles);

	  if (options.drawBorders && $.isArray(options.createBorders))
		createBorders(options.createBorders);

	  //}}}

	  // This is a hack for iOS5 to support drag/move touch functionality
	  $(document).bind('touchstart.jcrop-ios',function(e) {
		if ($(e.currentTarget).hasClass('jcrop-tracker')) e.stopPropagation();
	  });

	  var $track = newTracker().mousedown(createDragger('move')).css({
		cursor: 'move',
		position: 'absolute',
		zIndex: 360
	  });

	  if (Touch.support) {
		$track.bind('touchstart.jcrop', Touch.createDragger('move'));
	  }

	  $img_holder.append($track);
	  disableHandles();

	  return {
		updateVisible: updateVisible,
		update: update,
		release: release,
		refresh: refresh,
		isAwake: function () {
		  return awake;
		},
		setCursor: function (cursor) {
		  $track.css('cursor', cursor);
		},
		enableHandles: enableHandles,
		enableOnly: function () {
		  seehandles = true;
		},
		showHandles: showHandles,
		disableHandles: disableHandles,
		animMode: animMode,
		setBgOpacity: setBgOpacity,
		done: done
	  };
	}());
	
	//}}}
	// Tracker Module {{{
	var Tracker = (function () {
	  var onMove = function () {},
		  onDone = function () {},
		  trackDoc = options.trackDocument;

	  function toFront() //{{{
	  {
		$trk.css({
		  zIndex: 450
		});
		if (Touch.support) {
		  $(document)
			.bind('touchmove.jcrop', trackTouchMove)
			.bind('touchend.jcrop', trackTouchEnd);
		}
		if (trackDoc) {
		  $(document)
			.bind('mousemove.jcrop',trackMove)
			.bind('mouseup.jcrop',trackUp);
		}
	  } 
	  //}}}
	  function toBack() //{{{
	  {
		$trk.css({
		  zIndex: 290
		});
		$(document).unbind('.jcrop');
	  } 
	  //}}}
	  function trackMove(e) //{{{
	  {
		onMove(mouseAbs(e));
		return false;
	  } 
	  //}}}
	  function trackUp(e) //{{{
	  {
		e.preventDefault();
		e.stopPropagation();

		if (btndown) {
		  btndown = false;

		  onDone(mouseAbs(e));

		  if (Selection.isAwake()) {
			options.onSelect.call(api, unscale(Coords.getFixed()));
		  }

		  toBack();
		  onMove = function () {};
		  onDone = function () {};
		}

		return false;
	  }
	  //}}}
	  function activateHandlers(move, done) //{{{
	  {
		btndown = true;
		onMove = move;
		onDone = done;
		toFront();
		return false;
	  }
	  //}}}
	  function trackTouchMove(e) //{{{
	  {
		e.pageX = e.originalEvent.changedTouches[0].pageX;
		e.pageY = e.originalEvent.changedTouches[0].pageY;
		return trackMove(e);
	  }
	  //}}}
	  function trackTouchEnd(e) //{{{
	  {
		e.pageX = e.originalEvent.changedTouches[0].pageX;
		e.pageY = e.originalEvent.changedTouches[0].pageY;
		return trackUp(e);
	  }
	  //}}}
	  function setCursor(t) //{{{
	  {
		$trk.css('cursor', t);
	  }
	  //}}}

	  if (!trackDoc) {
		$trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp);
	  }

	  $img.before($trk);
	  return {
		activateHandlers: activateHandlers,
		setCursor: setCursor
	  };
	}());
	//}}}
	// KeyManager Module {{{
	var KeyManager = (function () {
	  var $keymgr = $('<input type="radio" />').css({
		position: 'fixed',
		left: '-120px',
		width: '12px'
	  }),
		  $keywrap = $('<div />').css({
		  position: 'absolute',
		  overflow: 'hidden'
		}).append($keymgr);

	  function watchKeys() //{{{
	  {
		if (options.keySupport) {
		  $keymgr.show();
		  $keymgr.focus();
		}
	  }
	  //}}}
	  function onBlur(e) //{{{
	  {
		$keymgr.hide();
	  }
	  //}}}
	  function doNudge(e, x, y) //{{{
	  {
		if (options.allowMove) {
		  Coords.moveOffset([x, y]);
		  Selection.updateVisible(true);
		}
		e.preventDefault();
		e.stopPropagation();
	  }
	  //}}}
	  function parseKey(e) //{{{
	  {
		if (e.ctrlKey || e.metaKey) {
		  return true;
		}
		shift_down = e.shiftKey ? true : false;
		var nudge = shift_down ? 10 : 1;

		switch (e.keyCode) {
		case 37:
		  doNudge(e, -nudge, 0);
		  break;
		case 39:
		  doNudge(e, nudge, 0);
		  break;
		case 38:
		  doNudge(e, 0, -nudge);
		  break;
		case 40:
		  doNudge(e, 0, nudge);
		  break;
		case 27:
		  if (options.allowSelect) Selection.release();
		  break;
		case 9:
		  return true;
		}

		return false;
	  }
	  //}}}

	  if (options.keySupport) {
		$keymgr.keydown(parseKey).blur(onBlur);
		if (ie6mode || !options.fixedSupport) {
		  $keymgr.css({
			position: 'absolute',
			left: '-20px'
		  });
		  $keywrap.append($keymgr).insertBefore($img);
		} else {
		  $keymgr.insertBefore($img);
		}
	  }


	  return {
		watchKeys: watchKeys
	  };
	}());
	//}}}
	// }}}
	// API methods {{{
	function setClass(cname) //{{{
	{
	  $div.removeClass().addClass(cssClass('holder')).addClass(cname);
	}
	//}}}
	function animateTo(a, callback) //{{{
	{
	  var x1 = a[0] / xscale,
		  y1 = a[1] / yscale,
		  x2 = a[2] / xscale,
		  y2 = a[3] / yscale;

	  if (animating) {
		return;
	  }

	  var animto = Coords.flipCoords(x1, y1, x2, y2),
		  c = Coords.getFixed(),
		  initcr = [c.x, c.y, c.x2, c.y2],
		  animat = initcr,
		  interv = options.animationDelay,
		  ix1 = animto[0] - initcr[0],
		  iy1 = animto[1] - initcr[1],
		  ix2 = animto[2] - initcr[2],
		  iy2 = animto[3] - initcr[3],
		  pcent = 0,
		  velocity = options.swingSpeed;

	  x = animat[0];
	  y = animat[1];
	  x2 = animat[2];
	  y2 = animat[3];

	  Selection.animMode(true);
	  var anim_timer;

	  function queueAnimator() {
		window.setTimeout(animator, interv);
	  }
	  var animator = (function () {
		return function () {
		  pcent += (100 - pcent) / velocity;

		  animat[0] = x + ((pcent / 100) * ix1);
		  animat[1] = y + ((pcent / 100) * iy1);
		  animat[2] = x2 + ((pcent / 100) * ix2);
		  animat[3] = y2 + ((pcent / 100) * iy2);

		  if (pcent >= 99.8) {
			pcent = 100;
		  }
		  if (pcent < 100) {
			setSelectRaw(animat);
			queueAnimator();
		  } else {
			Selection.done();
			if (typeof(callback) === 'function') {
			  callback.call(api);
			}
		  }
		};
	  }());
	  queueAnimator();
	}
	//}}}
	function setSelect(rect) //{{{
	{
	  setSelectRaw([rect[0] / xscale, rect[1] / yscale, rect[2] / xscale, rect[3] / yscale]);
	  options.onSelect.call(api, unscale(Coords.getFixed()));
	  Selection.enableHandles();
	}
	//}}}
	function setSelectRaw(l) //{{{
	{
	  Coords.setPressed([l[0], l[1]]);
	  Coords.setCurrent([l[2], l[3]]);
	  Selection.update();
	}
	//}}}
	function tellSelect() //{{{
	{
	  return unscale(Coords.getFixed());
	}
	//}}}
	function tellScaled() //{{{
	{
	  return Coords.getFixed();
	}
	//}}}
	function setOptionsNew(opt) //{{{
	{
	  setOptions(opt);
	  interfaceUpdate();
	}
	//}}}
	function disableCrop() //{{{
	{
	  options.disabled = true;
	  Selection.disableHandles();
	  Selection.setCursor('default');
	  Tracker.setCursor('default');
	}
	//}}}
	function enableCrop() //{{{
	{
	  options.disabled = false;
	  interfaceUpdate();
	}
	//}}}
	function cancelCrop() //{{{
	{
	  Selection.done();
	  Tracker.activateHandlers(null, null);
	}
	//}}}
	function destroy() //{{{
	{
	  $div.remove();
	  $origimg.show();
	  $(obj).removeData('Jcrop');
	}
	//}}}
	function setImage(src, callback) //{{{
	{
	  Selection.release();
	  disableCrop();
	  var img = new Image();
	  img.onload = function () {
		var iw = img.width;
		var ih = img.height;
		var bw = options.boxWidth;
		var bh = options.boxHeight;
		$img.width(iw).height(ih);
		$img.attr('src', src);
		$img2.attr('src', src);
		presize($img, bw, bh);
		boundx = $img.width();
		boundy = $img.height();
		$img2.width(boundx).height(boundy);
		$trk.width(boundx + (bound * 2)).height(boundy + (bound * 2));
		$div.width(boundx).height(boundy);
		Shade.resize(boundx,boundy);
		enableCrop();

		if (typeof(callback) === 'function') {
		  callback.call(api);
		}
	  };
	  img.src = src;
	}
	//}}}
	function colorChangeMacro($obj,color,now) {
	  var mycolor = color || options.bgColor;
	  if (options.bgFade && supportsColorFade() && options.fadeTime && !now) {
		$obj.animate({
		  backgroundColor: mycolor
		}, {
		  queue: false,
		  duration: options.fadeTime
		});
	  } else {
		$obj.css('backgroundColor', mycolor);
	  }
	}
	function interfaceUpdate(alt) //{{{
	// This method tweaks the interface based on options object.
	// Called when options are changed and at end of initialization.
	{
	  if (options.allowResize) {
		if (alt) {
		  Selection.enableOnly();
		} else {
		  Selection.enableHandles();
		}
	  } else {
		Selection.disableHandles();
	  }

	  Tracker.setCursor(options.allowSelect ? 'crosshair' : 'default');
	  Selection.setCursor(options.allowMove ? 'move' : 'default');

	  if (options.hasOwnProperty('trueSize')) {
		xscale = options.trueSize[0] / boundx;
		yscale = options.trueSize[1] / boundy;
	  }

	  if (options.hasOwnProperty('setSelect')) {
		setSelect(options.setSelect);
		Selection.done();
		delete(options.setSelect);
	  }

	  Shade.refresh();

	  if (options.bgColor != bgcolor) {
		colorChangeMacro(
		  options.shade? Shade.getShades(): $div,
		  options.shade?
			(options.shadeColor || options.bgColor):
			options.bgColor
		);
		bgcolor = options.bgColor;
	  }

	  if (bgopacity != options.bgOpacity) {
		bgopacity = options.bgOpacity;
		if (options.shade) Shade.refresh();
		  else Selection.setBgOpacity(bgopacity);
	  }

	  xlimit = options.maxSize[0] || 0;
	  ylimit = options.maxSize[1] || 0;
	  xmin = options.minSize[0] || 0;
	  ymin = options.minSize[1] || 0;

	  if (options.hasOwnProperty('outerImage')) {
		$img.attr('src', options.outerImage);
		delete(options.outerImage);
	  }

	  Selection.refresh();
	}
	//}}}
	//}}}

	if (Touch.support) $trk.bind('touchstart.jcrop', Touch.newSelection);

	$hdl_holder.hide();
	interfaceUpdate(true);

	var api = {
	  setImage: setImage,
	  animateTo: animateTo,
	  setSelect: setSelect,
	  setOptions: setOptionsNew,
	  tellSelect: tellSelect,
	  tellScaled: tellScaled,
	  setClass: setClass,

	  disable: disableCrop,
	  enable: enableCrop,
	  cancel: cancelCrop,
	  release: Selection.release,
	  destroy: destroy,

	  focus: KeyManager.watchKeys,

	  getBounds: function () {
		return [boundx * xscale, boundy * yscale];
	  },
	  getWidgetSize: function () {
		return [boundx, boundy];
	  },
	  getScaleFactor: function () {
		return [xscale, yscale];
	  },
	  getOptions: function() {
		// careful: internal values are returned
		return options;
	  },

	  ui: {
		holder: $div,
		selection: $sel
	  }
	};

	if ($.browser.msie)
	  $div.bind('selectstart', function () { return false; });

	$origimg.data('Jcrop', api);
	return api;
  };
  $.fn.Jcrop = function (options, callback) //{{{
  {
	var api;
	// Iterate over each object, attach Jcrop
	this.each(function () {
	  // If we've already attached to this object
	  if ($(this).data('Jcrop')) {
		// The API can be requested this way (undocumented)
		if (options === 'api') return $(this).data('Jcrop');
		// Otherwise, we just reset the options...
		else $(this).data('Jcrop').setOptions(options);
	  }
	  // If we haven't been attached, preload and attach
	  else {
		if (this.tagName == 'IMG')
		  $.Jcrop.Loader(this,function(){
			$(this).css({display:'block',visibility:'hidden'});
			api = $.Jcrop(this, options);
			if ($.isFunction(callback)) callback.call(api);
		  });
		else {
		  $(this).css({display:'block',visibility:'hidden'});
		  api = $.Jcrop(this, options);
		  if ($.isFunction(callback)) callback.call(api);
		}
	  }
	});

	// Return "this" so the object is chainable (jQuery-style)
	return this;
  };
  //}}}
  // $.Jcrop.Loader - basic image loader {{{

  $.Jcrop.Loader = function(imgobj,success,error){
	var $img = $(imgobj), img = $img[0];

	function completeCheck(){
	  if (img.complete) {
		$img.unbind('.jcloader');
		if ($.isFunction(success)) success.call(img);
	  }
	  else window.setTimeout(completeCheck,50);
	}

	$img
	  .bind('load.jcloader',completeCheck)
	  .bind('error.jcloader',function(e){
		$img.unbind('.jcloader');
		if ($.isFunction(error)) error.call(img);
	  });

	if (img.complete && $.isFunction(success)){
	  $img.unbind('.jcloader');
	  success.call(img);
	}
  };

  //}}}
  // Global Defaults {{{
  $.Jcrop.defaults = {

	// Basic Settings
	allowSelect: true,
	allowMove: true,
	allowResize: true,

	trackDocument: true,

	// Styling Options
	baseClass: 'jcrop',
	addClass: null,
	bgColor: 'black',
	bgOpacity: 0.6,
	bgFade: false,
	borderOpacity: 0.4,
	handleOpacity: 0.5,
	handleSize: 7,

	aspectRatio: 0,
	keySupport: true,
	createHandles: ['n','s','e','w','nw','ne','se','sw'],
	createDragbars: ['n','s','e','w'],
	createBorders: ['n','s','e','w'],
	drawBorders: true,
	dragEdges: true,
	fixedSupport: true,
	touchSupport: null,

	shade: null,

	boxWidth: 0,
	boxHeight: 0,
	boundary: 2,
	fadeTime: 400,
	animationDelay: 20,
	swingSpeed: 3,

	minSelect: [0, 0],
	maxSize: [0, 0],
	minSize: [0, 0],

	// Callbacks / Event Handlers
	onChange: function () {},
	onSelect: function () {},
	onDblClick: function () {},
	onRelease: function () {}
  };

  // }}}
}(jQuery));
